package at.jaki.ubb.crawling.demo.db;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

import org.apache.log4j.Logger;

import at.jaki.ubb.crawling.demo.model.StarData;
import at.jaki.ubb.crawling.demo.utils.FileUtil;
import at.jaki.ubb.crawling.model.CEEntity;

public final class FsDataBase {

	private static final Logger LOG = Logger.getLogger(FsDataBase.class);
	private static final String PATH = "stars";
	private static final String CATALOG_FILE = "catalog.scat";
	private static final String EXT = ".sd";
	private static final String DISPLAY_PATTERN = "(%d) %s";

	private static FsDataBase instance;

	public static synchronized FsDataBase getInstance() {
		if (instance == null) {
			instance = new FsDataBase();
		}
		return instance;
	}

	@Override
	protected Object clone() throws CloneNotSupportedException {
		throw new CloneNotSupportedException();
	}

	/**
	 * id,path
	 */
	private Map<String, String> fsStarCatalog;
	private Map<String, StarData> newStars = new HashMap<String, StarData>();

	private FsDataBase() {
		initFs();
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private void initFs() {
		File folder = new File(PATH);
		if (!folder.isDirectory()) {
			LOG.info("creating folder 'stars'");
			folder.mkdir();
			LOG.info("creating empty catalog");
			fsStarCatalog = new TreeMap<String, String>();
		} else {
			ObjectInputStream ois = null;
			try {
				ois = new ObjectInputStream(new FileInputStream(new File(folder, CATALOG_FILE)));
				LOG.info("read catalog from file");
			} catch (FileNotFoundException e) {
				LOG.warn("catalog file not found");
			} catch (IOException e) {
				LOG.error("error reading catalog: " + e.getClass().getName() + " " + e.getMessage());
			}
			if (ois != null) {
				try {
					fsStarCatalog = (Map) ois.readObject();
				} catch (IOException e) {
					LOG.error("error reading catalog: " + e.getClass().getName() + " " + e.getMessage());
				} catch (ClassNotFoundException e) {
					LOG.error("error reading catalog: " + e.getClass().getName() + " " + e.getMessage());
				}
			}
			if (fsStarCatalog == null) {
				LOG.info("Createing catalog");
				fsStarCatalog = new TreeMap<String, String>();
				File[] listFiles = folder.listFiles();
				for (File f : listFiles) {
					try {
						StarData sd = FileUtil.readStarFromFile(f);
						fsStarCatalog.put(getDisplay(sd), f.getAbsolutePath());
						LOG.info("read data of " + sd.getId());
					} catch (Exception e) {
						LOG.warn("failed to read data from " + f.getAbsolutePath());
						LOG.warn(e.getClass().getName() + " " + e.getMessage());
					}
				}
			}
		}
	}
	
	public synchronized void saveTemp(CEEntity entity){
		if (!(entity instanceof StarData)) {
			LOG.error("Won't save not star data");
		}
		StarData sd = (StarData) entity;
		try {
			if (sd.getGuid() != null) {
				newStars.put(sd.getGuid(), sd);
				LOG.info("star data saved in RAM");
			}
		} catch (Exception e) {
			LOG.error("Erorr saving " + sd.getId() + " " + e.getClass().getName() + " " + e.getMessage());
		}
		
	}

	public synchronized void save(StarData sd) {
		try {
			File file = new File(PATH, sd.getId() + EXT);
			FileUtil.writeStarToFile(file, sd);
			LOG.info("saved " + sd.getId() + " to file");
			fsStarCatalog.put(getDisplay(sd), file.getAbsolutePath());
			LOG.info("updated catalog");
		} catch (Exception e) {
			LOG.error("Erorr saving " + sd.getId() + " " + e.getClass().getName() + " " + e.getMessage());
		}
	}

	public void saveCatalog() {
		try {
			ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File(PATH, CATALOG_FILE)));
			oos.writeObject(fsStarCatalog);
		} catch (Exception e) {
			LOG.error("Could not save catalog");
			LOG.error(e.getClass().getName() + " " + e.getMessage());
		}
	}

	public StarData getStarDataByGUID(String guid) throws FileNotFoundException, IOException, ClassNotFoundException {
		StarData sd = newStars.get(guid);
		newStars.remove(guid);
		return sd;
	}

	public static String getDisplay(StarData sd) {
		return String.format(DISPLAY_PATTERN, sd.getMagicNumber(), sd.getId());
	}

	public Collection<String> getAllIds() {
		return fsStarCatalog.keySet();
	}

	public StarData getById(String selectedId) throws FileNotFoundException, IOException, ClassNotFoundException {
		String string = fsStarCatalog.get(selectedId);
		return FileUtil.readStarFromFile(new File(string));
	}
}
